/**
 * f-club.cn
 * Copyright (c) 2009-2012 All Rights Reserved.
 */
package com.mtoolkit.socket.client;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import com.mtoolkit.socket.SocketRequest;
import com.mtoolkit.socket.SocketResponse;
import com.mtoolkit.socket.transocder.DefaultSocketRequestTranscoder;
import com.mtoolkit.socket.transocder.DefaultSocketResponseTranscoder;
import com.mtoolkit.socket.transocder.SocketRequestTranscoder;
import com.mtoolkit.socket.transocder.SocketResponseTranscoder;
import com.mtoolkit.util.LogUtil;

/**
 * Socket client.
 * 
 * @author michael
 * @version $Id: SocketClient.java 71 2012-10-29 13:55:50Z xishizhang@gmail.com $
 */
public class SocketClient {
	
	private Socket _client;
	private SocketRequestTranscoder _requestTranstor = new DefaultSocketRequestTranscoder();
	private SocketResponseTranscoder _responseTranstor = new DefaultSocketResponseTranscoder();
	
	private static final int EOF = -1;
	
	private static final LogUtil LOGGER = LogUtil.getLogger(SocketClient.class);
	
	public SocketClient(Socket client) {
		_client = client;
	}
	
	public SocketClient(String host, int port) throws UnknownHostException, IOException {
		this(new Socket(host, port));
	}
	
	public void setSocketRequestTranstor(SocketRequestTranscoder requestTranstor) {
		if (requestTranstor == null) {
			throw new NullPointerException("requestTranstor");
		}
		_requestTranstor = requestTranstor;
	}
	
	public void setSocketResponseTranstor(SocketResponseTranscoder responseTranstor) {
		if (responseTranstor == null) {
			throw new NullPointerException("responseTranstor");
		}
		_responseTranstor = responseTranstor;
	}
	
	public void send(SocketRequest request) throws IOException {
		byte[] message = _requestTranstor.sealRequest(request);
		System.err.println("send message length: " + message.length);
		_client.getOutputStream().write(message);
		_client.getOutputStream().flush();
	}
	
	public SocketResponse receive() throws IOException, ClassNotFoundException {
		try {
			return receive(-1L, null);
		} catch (TimeoutException e) {
			// TODO ignore
			return null;
		}
	}
	
	public SocketResponse receive(long timeout, TimeUnit unit) throws IOException, TimeoutException, ClassNotFoundException {
		long timeoutNanos = 0L;
		if (timeout > 0L) {
			timeoutNanos = unit.toNanos(timeout);
		}
		
		InputStream input = _client.getInputStream();
		ByteArrayOutputStream output = new ByteArrayOutputStream();
		
		final long startTime = System.nanoTime();
		SocketResponse response = null;
		final byte[] buffer = new byte[1024];
		while (true) {
			int read = input.read(buffer);
			if (read == EOF) {
				close();
			} else {
				if (System.nanoTime() - startTime >= timeoutNanos) {
					throw new TimeoutException("SocketClient receive response timeout.");
				}

				output.write(buffer, 0, read);
				response = _responseTranstor.unsealResponse(output.toByteArray());
				if (response != null) {
					break;
				}
			}
		}

		LOGGER.debug("Socket client got response [{0}] by request id [{1}]", 
				response.getResponseData(), response.getRequestId());
		return response;
	}
	
	public void close() throws IOException {
	    _client.getOutputStream().close();
	    _client.getInputStream().close();
	    _client.close();
	}
	
}
